跳到主要内容

Kubernetes 理解部署一个服务的过程

资源管理介绍

在 Kubernetes 中,所有的内容都抽象为资源,用户需要通过操作资源来管理 Kubernetes。

1、Kubernetes 的本质就是一个集群系统,用户可以在集群中部署各种服务。所谓的部署服务,其实就是在 Kubernetes 集群中运行一个个的容器,并将指定的程序跑在容器中。

2、Kubernetes 的最小管理单元是 Pod 而不是容器,所以只能将容器放在 Pod 中,而 Kubernetes 一般也不会直接管理 Pod,而是通过 Pod 控制器来管理 Pod 的。

3、Pod 提供服务之后,就需要考虑如何访问 Pod 中的服务,Kubernetes 提供了 Service 资源实现这个功能。

Pods 和 Nodes 的关系如下图所示:

学习 Kubernetes 的核心,就是学习如何对集群中的 Pod、Pod 控制器、Service、存储等各种资源进行操作。

有状态服务 VS 无状态服务

一般来说,业务的服务类型可分为无状态服务和有状态服务。判断的关键在于,两个来自相同发起者的请求在服务器端是否具备上下文关系。

  • 如果是有状态服务,其请求是状态化的,服务器端需要保存请求的相关信息,这样每个请求都可以默认地使用之前的请求上下文。

  • 而无状态服务就不需要这样,每次请求都包含了需要的所有信息,每次请求都和之前的没有任何关系。

有状态服务和无状态服务分别有各自擅长的业务类型和技术优势,在 Kubernetes 中,分别有不同的工作负载控制器来负责承载这两类服务。

工作负载控制器

工作负载组件结构图

当一个 Pod 被创建出来,不管是由你直接创建,还是由其他工作负载控制器(Workload Controller)自动创建,经过调度器调度以后,就永久地“长”在某个节点上了,直到该 Pod 被删除,或者因为资源不够被驱逐,抑或由于对应的节点故障导致宕机等。因此单独地用一个 Pod 来承载业务,是没办法保证高可用、可伸缩、负载均衡等要求,而且 Pod 也无法“自愈”。

这时我们就需要在 Pod 之上做一层抽象,通过多个副本(Replica)来保证可用 Pod 的数量,避免业务不可用。

上面也介绍了,不同的应用是有不同性质的,所以 Kubernetes 也提供了不同的工作负载控制器来管理不同类型的应用。

  • Deployment: 无状态应用部署,比如,微服务,提供多副本等功能
  • StatefulSet: 有状态应用部署,比如redis,提供稳定的存储,网络等功能
  • DaemonSet:守护型应用部署,比如日志收集组件,在每个机器都运行一份
  • Job/CronJob:定时任务部署,比如垃圾清理组件,可以在指定时间运行

补充:Pod 对象的抽象

Kubernetes 中各个对象的 metadata 字段都有 label(标签)annotation(注解) 两个对象,可以用来标识一些元数据信息。不论是 label 还是 annotation,都是一组键值对,键值不允许相同。

  • annotation 为对象附加任意的非标识的元数据,其并不用于标识和选择对象,客户端程序(例如工具和库)能够获取这些元数据信息。
  • label 主要用来标识一些有意义且和对象密切相关的信息,用来支持 labelSelector(标签选择器)以及一些查询操作,还有选择对象。

为了让这种抽象的对象可以跟 Pod 关联起来,Kubernetes 使用了labelSelector来跟 label 进行选择匹配,从而达到这种松耦合的关联效果。

$ kubectl get pod -l label1=value1,label2=value2 -n my-namespace

比如,我们就可以通过上述命令,查询出 my-namespace 这个命名空间下面,带有标签 label1=value1label2=value2 的 pod。label 中的键值对在匹配的时候是 “且” 的关系。

Kubernetes 中的无状态工作负载

每一个 Pod 都是应用的一个实例,但是通常来说你不会直接在 Kubernetes 中创建和运行单个 Pod。因为 Pod 的生命周期是短暂的,即 “用后即焚”。理解这一点很重要,这也是 “不可变基础设施” 这一理念在 Kubernetes 中的最佳实践。

Pod 实例是由谁创建的 ?

Kubernetes 中有一系列的工作负载可以用来部署无状态服务。在最初,Kubernetes 中使用了 ReplicationController 来做 Pod 的副本控制,即确保该服务的 Pod 数量维持在特定的数量。

ReplicationController 的代替者

在新版本的 Kubernetes 中建议使用 ReplicaSet 来取代 ReplicationController。ReplicaSet 跟 ReplicationController 没有本质的不同,只是名字不一样,并且 ReplicaSet 支持集合式的 selector。

虽然 ReplicaSet 可以独立使用,但一般还是建议使用 Deployment 来自动管理 ReplicaSet,这样就无需担心跟其他机制的不兼容问题(比如 ReplicaSet 不支持 rolling-update 但 Deployment 支持)。

如下所示

# ReplicationController 已经被 deployment 和 ReplicaSet 代替了
$ kubectl get ReplicaSet
NAME DESIRED CURRENT READY AGE
nginx-7cbb8cd5d8 1 1 1 14d

DESIRED 列显示了希望 ReplicaSet 保持的 pod 副本数, 而 CURRENT 列显示当前运行的 pod数。

使用 Deployment 部署服务

通过 Deployment,我们就不需要再关心和操作 ReplicaSet 了。

Deployment、ReplicaSet 和 Pod 这三者之间的关系见上图。

通过 Deployment,我们可以管理多个 label 各不相同的 ReplicaSet,每个 ReplicaSet 负责保证对应数目的 Pod 在运行。

我们来看一个定义 Deployment 的例子:

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-demo
namespace: demo
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
version: v1
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80

我们这里定义了副本数 spec.replicas 为 3,同时在 spec.selector.matchLabels 中设置了 app=nginx 的 label,用于匹配 spec.template.metadata.labels 的 label。我们还增加了 version=v1 的 label 用于后续滚动升级做比较。

我们将上述内容保存到 deploy-demo.yaml 文件中。

注意,spec.selector.matchLabels 中写的 label 一定要能匹配得了 spec.template.metadata.labels 中的 label。否则该 Deployment 无法关联创建出来的 ReplicaSet。

然后我们通过下面这些命令,便可以在命名空间 demo 中创建名为 nginx-deployment-demo 的 Deployment。

$ kubectl create ns demo
$ kubectl create -f deploy-demo.yaml
deployment.apps/nginx-deployment-demo created

创建完成后,我们可以查看自动创建出来的 rs。

DESIRED 列显示了希望 ReplicaSet 保持的 pod 副本数, 而 CURRENT 列显示当前运行的 Pod 数。

接下来,我们可以通过如下的几个命令不断地查询系统,看看对应的 Pod 是否运行成功。

$ kubectl get pod -n demo -l app=nginx,version=v1
NAME READY STATUS RESTARTS AGE
nginx-deployment-demo-6974d66d6f-5njxp 0/1 ContainerCreating 0 96s
nginx-deployment-demo-6974d66d6f-78mvs 0/1 ContainerCreating 0 96s
nginx-deployment-demo-6974d66d6f-vr45s 0/1 ContainerCreating 0 96s


# 一段时间后
$ kubectl get pod -n demo -l app=nginx,version=v1
NAME READY STATUS RESTARTS AGE
nginx-deployment-demo-6974d66d6f-5njxp 1/1 Running 0 2m34s
nginx-deployment-demo-6974d66d6f-78mvs 1/1 Running 0 2m34s
nginx-deployment-demo-6974d66d6f-vr45s 1/1 Running 0 2m34s

也可以更改 deploy-demo.yaml 这个文件:

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-demo
namespace: demo
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
version: v2
spec:
containers:
- name: nginx
image: nginx:1.19.2
ports:
- containerPort: 80

更新 nginx 的版本,然后命令:

$ kubectl apply -f deploy-demo.yaml

这个时候,我们来看看 ReplicaSet 有什么变化:

$ kubectl get rs -n demo

可以看到,这个时候自动创建了一个新的 rs 出来:nginx-deployment-demo-7bd5846bf。一般 Deployment 的默认更新策略是 RollingUpdate,即先创建一个新的 Pod,并待其成功运行以后,再删除旧的。

后面 Deployment 控制器会不断对这两个 rs 进行调和,直到新的 rs 副本数为 3,老的 rs 副本数为 0。

隔一段时间后再次执行命令可以发现,新的 rs 副本数为 3,老的 rs 副本数为 0。

Kubernetes 中的有状态工作负载

上面利用无状态负载器 Deployment 来创建的 Pod,虽然每个 Pod 都会分配一个单独的 Pod 的 IP 地址,但是却存在如下的问题:

  • Pod 的 IP 会随着 Pod 的重建产生变化。
  • Pod 的 IP 仅仅是集群内部可见的虚拟的 IP,外部无法访问。

这样对于访问这个服务带来了难度,因此,kubernetes 设计了 Kubernetes 中的另外一种工作负载 StatefulSet。

从名字就可以看出,这个工作负载主要用于有状态的服务发布。一般将 StatefulSet 简写为 sts,在部署一个 StatefulSet 的时候,有个前置依赖对象,即 Service(服务)。

Service 服务的概念

一个 pod 可能会在任何时候消失,或许因为它所在节点发生故障,或许因为有人删除了 pod,或者因为 pod 被从一个健康的节点剔除了。当其中任何一种情况发生时,如前所述,消失的 pod 将被 ReplicationController 替换为新的 pod。新的 pod 与替换它的 pod 具有不同的 IP 地址。

所以一个服务可能由不同的 Pod 来提供,它们之间并不是强绑定的,当一个 Pod 被释放,ReplicationController 可能会创建一个新的 Pod 来提供服务,因此通过服务这个抽象,解决了不断变化的 Pod IP 地址的问题,以及在一个固定的 IP 和端口上对外暴露多个 pod。

当一个服务被创建时,它会得到一个静态的 IP,在服务的生命周期中这个 IP 不会发生改变。客户端应该通过固定 IP 地址连接到服务,而不是直接连接 pod。服务会确保其中一个 pod 接收连接,而不关心 pod 当前运行在哪里(以及它的 IP 地址是什么)。

如上图这个 kubia-http 服务,它不管内部 IP 怎么变,对外始终暴露的是 104.155.74.57 这个 IP 地址

定义一个服务

如下定义一个服务

# nginx-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-demo
namespace: demo
labels:
app: nginx
spec:
clusterIP: None
ports:
- port: 80
name: web
selector:
app: nginx

上面这段 yaml 的意思是,在 demo 这个命名空间中,创建一个名为 nginx-demo 的服务,这个服务暴露了 80 端口,可以访问带有 app=nginx 这个 label 的 Pod。

我们现在利用上面这段 yaml 在集群中创建出一个 Service:

$ kubectl create ns demo
$ kubectl create -f nginx-svc.yaml
service/nginx-demo created
$ kubectl get svc -n demo
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-demo ClusterIP None <none> 80/TCP 5s

创建好了这个前置依赖的 Service,下面我们就可以开始创建真正的 StatefulSet 对象,可参照如下的 yaml 文件:

# web-sts.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web-demo
namespace: demo
spec:
serviceName: "nginx-demo"
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.19.2-alpine
ports:
- containerPort: 80
name: web

创建真正的 StatefulSet 对象

$ kubectl create -f web-sts.yaml
$ kubectl get sts -n demo
NAME READY AGE
web-demo 0/2 9s

可以看到,到这里我已经将名为 web-demo 的 StatefulSet 部署完成了。

下面我们来一点点探索 StatefulSet 的秘密,看看它有哪些特性,为何可以保障服务有状态的运行。

StatefulSet 的特性

StatefulSet 的特点:

  • 具备固定的网络标记,比如主机名,域名等;
  • 支持持久化存储,而且最好能够跟实例一一绑定;
  • 可以按照顺序来部署和扩展;
  • 可以按照顺序进行终止和删除操作;
  • 在进行滚动升级的时候,也会按照一定顺序。

借助 StatefulSet 的这些能力,我们就可以去部署一些有状态服务,比如 MySQL、ZooKeeper、MongoDB 等。

StatefulSet 的特性测试

下面对 StatefulSet 的特性进行测试,看看它是如何保障服务有状态的运行的。

通过 kubectl 的 watch 功能(命令行加参数 -w),我们可以观察到 Pod 状态的一步步变化。

$ kubectl get pod -n demo -w
NAME READY STATUS RESTARTS AGE
web-demo-0 0/1 ContainerCreating 0 18s
web-demo-0 1/1 Running 0 20s
web-demo-1 0/1 Pending 0 0s
web-demo-1 0/1 Pending 0 0s
web-demo-1 0/1 ContainerCreating 0 0s
web-demo-1 1/1 Running 0 2s

通过 StatefulSet 创建出来的 Pod 名字有一定的规律,即 $(statefulset名称)-$(序号),比如这个例子中的 web-demo-0、web-demo-1。

这里面还有个有意思的点,web-demo-0 这个 Pod 比 web-demo-1 优先创建,而且在 web-demo-0 变为 Running 状态以后,才被创建出来。

为了证实这个猜想,我们在一个终端窗口观察 StatefulSet 的 Pod:

$ kubectl get pod -n demo -w -l app=nginx

我们再开一个终端端口来 watch 这个 namespace 中的 event:

$ kubectl get event -n demo -w

现在我们试着改变这个 StatefulSet 的副本数,将它改成 5:

$ kubectl scale sts web-demo -n demo --replicas=5
statefulset.apps/web-demo scaled

此时我们观察到另外两个终端端口的输出:

$ kubectl get pod -n demo -w
NAME READY STATUS RESTARTS AGE
web-demo-0 1/1 Running 0 20m
web-demo-1 1/1 Running 0 20m
web-demo-2 0/1 Pending 0 0s
web-demo-2 0/1 Pending 0 0s
web-demo-2 0/1 ContainerCreating 0 0s
web-demo-2 1/1 Running 0 2s
web-demo-3 0/1 Pending 0 0s
web-demo-3 0/1 Pending 0 0s
web-demo-3 0/1 ContainerCreating 0 0s
web-demo-3 1/1 Running 0 3s
web-demo-4 0/1 Pending 0 0s
web-demo-4 0/1 Pending 0 0s
web-demo-4 0/1 ContainerCreating 0 0s
web-demo-4 1/1 Running 0 3s

我们再一次看到了 StatefulSet 管理的 Pod 按照 2、3、4 的顺序依次创建,名称有规律,跟上一节通过 Deployment 创建的随机 Pod 名有很大的区别。

可见,对于一个拥有 N 个副本的 StatefulSet 来说,Pod 在部署时按照 {0 …… N-1} 的序号顺序创建的,而删除的时候按照逆序逐个删除,这便是我想说的第一个特性。

接着我们来看,StatefulSet 创建出来的 Pod 都具有固定的、且确切的主机名,比如:

$ for i in 0 1; do kubectl exec web-demo-$i -n demo -- sh -c 'hostname'; done
web-demo-0
web-demo-1

我们再看看上面 StatefulSet 的 API 对象定义,有没有发现跟我们上一节中 Deployment 的定义极其相似,主要的差异在于 spec.serviceName 这个字段。它很重要,StatefulSet 根据这个字段,为每个 Pod 创建一个 DNS 域名,这个域名的格式为 $(podname).(headless service name),下面我们通过例子来看一下。

当前 Pod 和 IP 之间的对应关系如下:

$ kubectl get pod -n demo -l app=nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-demo-0 1/1 Running 0 3h17m 10.244.0.39 kraken <none> <none>
web-demo-1 1/1 Running 0 3h17m 10.244.0.40 kraken <none> <none>

Podweb-demo-0 的 IP 地址是 10.244.0.39,web-demo-1 的 IP 地址是 10.244.0.40。这里通过 kubectl run 在同一个命名空间 demo 中创建一个名为 dns-test 的 Pod,同时 attach 到容器中,类似于 docker run -it --rm 这个命令。

在容器中运行 nslookup 来查询它们在集群内部的 DNS 地址,如下所示:

$ kubectl run -it --rm --image busybox:1.28 dns-test -n demo
If you don't see a command prompt, try pressing enter.
/ # nslookup web-demo-0.nginx-demo
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: web-demo-0.nginx-demo
Address 1: 10.244.0.39 web-demo-0.nginx-demo.demo.svc.cluster.local
/ # nslookup web-demo-1.nginx-demo
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: web-demo-1.nginx-demo
Address 1: 10.244.0.40 web-demo-1.nginx-demo.demo.svc.cluster.local

可以看到,每个 Pod 都有一个对应的 A 记录。 我们现在删除一下这些 Pod,看看会有什么变化:

$ kubectl delete pod -l app=nginx -n demo
pod "web-demo-0" deleted
pod "web-demo-1" deleted
$ kubectl get pod -l app=nginx -n demo -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-demo-0 1/1 Running 0 15s 10.244.0.50 kraken <none> <none>
web-demo-1 1/1 Running 0 13s 10.244.0.51 kraken <none> <none>

删除成功后,可以发现 StatefulSet 立即生成了新的 Pod,但是 Pod 名称维持不变。唯一变化的就是 IP 发生了改变。

我们再来看看 DNS 记录:

$ kubectl run -it --rm --image busybox:1.28 dns-test -n demo
If you don't see a command prompt, try pressing enter.
/ # nslookup web-demo-0.nginx-demo
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name: web-demo-0.nginx-demo
Address 1: 10.244.0.50 web-demo-0.nginx-demo.demo.svc.cluster.local
/ # nslookup web-demo-1.nginx-demo
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name: web-demo-1.nginx-demo
Address 1: 10.244.0.51 web-demo-1.nginx-demo.demo.svc.cluster.local

可以看出,DNS 记录中 Pod 的域名没有发生变化,仅仅 IP 地址发生了更换。因此当 Pod 所在的节点发生故障导致 Pod 飘移到其他节点上,或者 Pod 因故障被删除重建,Pod 的 IP 都会发生变化,但是 Pod 的域名不会有任何变化,这也就意味着服务间可以通过不变的 Pod 域名来保障通信稳定,而不必依赖 Pod IP。

有了 spec.serviceName 这个字段,保证了 StatefulSet 关联的 Pod 可以有稳定的网络身份标识,即 Pod 的序号、主机名、DNS 记录名称等。

通过 yaml 配置方式

新建 svc-nginx.yaml,内容如下:

apiVersion: v1
kind: Service
metadata:
name: svc-nginx
namespace: dev
spec:
clusterIP: 10.109.179.231
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
run: nginx # 这个表示具有 run=nginx 标签的 Pod 都属于这个服务
type: ClusterIP

Service 中通过 spec.selector 字段来选择一组 Pod,并将服务请求转发到选中的 Pod 上。

上面的例子中定义的标签选择器等价于 run=nginxrun in (nginx)

注意:这里的 spec.selector 下面是个 Map,即

spec:
selector:
key1: value1
key2: value2

执行创建和删除命令:

kubectl  create  -f  svc-nginx.yaml
kubectl delete -f svc-nginx.yaml

StatefulSet 使用 PVC

在 Kubernetes 中,可以使用 StatefulSet 来管理有状态的应用程序,例如数据库集群。与 Deployment 不同,StatefulSet 中的每个 Pod 都有唯一的标识符,即它们的名称,这些名称可以基于固定的命名约定自动创建。这使得它们可以更容易地进行有序的操作,例如扩展和缩小,因为它们的名称不会随机生成和更改。

当使用 StatefulSet 部署应用程序时,可以使用 PVC(Persistent Volume Claims)来管理 Pod 中的数据。PVC 充当 Pod 和存储之间的中介,并确保数据存储持久化,即使 Pod 进程终止或迁移。

在 StatefulSet 中,可以使用 volumeClaimTemplates 字段来定义 PVC 模板,以便每个 Pod 在创建时自动创建 PVC。以下是一个 StatefulSet 的 YAML 配置文件示例,其中使用 PVC 存储 Redis 数据库的数据:

apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-cluster
spec:
serviceName: redis-cluster
replicas: 3
selector:
matchLabels:
app: redis-cluster
template:
metadata:
labels:
app: redis-cluster
spec:
containers:
- name: redis
image: redis
volumeMounts:
- name: data
mountPath: /data
ports:
- containerPort: 6379
volumes:
- name: data
persistentVolumeClaim:
claimName: redis-data
volumeClaimTemplates:
- metadata:
name: redis-data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
storageClassName: local-path

在上述配置文件中,volumeClaimTemplates 字段定义了一个 PVC 模板,名称为 redis-data。在每个 Pod 中,定义了一个名为 data 的卷,并将其挂载到 /data 路径上。persistentVolumeClaim 字段用于指定要使用的 PVC,这里使用了 redis-data,它会根据 volumeClaimTemplates 中的定义自动创建。在这个例子中,使用了 local-path 存储类作为 PVC 的存储后端,该存储类已经配置为使用本地存储插件。

当创建 StatefulSet 时,Kubernetes 会自动创建三个 Pod,每个 Pod 都会自动创建一个 redis-data PVC。如果 Pod 被删除或重新创建,Kubernetes 会自动重新绑定相应的 PVC,确保 Pod 中的数据不会丢失。

Kubernetes 中的守护型应用部署

在使用 kubernetes 来运行应用时,很多时候我们需要在一个区域(zone)或者所有 Node 上 运行同一个守护进程(pod)。

DaemonSet 能够让所有(或者一些特定)的 Node 节点运行同一个 pod:

  • 当节点加入到 kubernetes 集群中,pod 会被(DaemonSet)调度到该节点上运行。
  • 当节点从 kubernetes 集群中被移除,被(DaemonSet)调度的 pod 会被移除,如果删除 DaemonSet,所有跟这个 DaemonSet 相关的 pods 都会被删除

例如如下场景:

每个 Node 上运行一个分布式存储的守护进程,例如 glusterd,ceph 每个 Node 上运行日志采集器,例如 fluentd,logstash

apiVersion: apps/v1
kind: DaemonSet
metadata:
name: ds1
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
selector:
matchLabels:
name: fluentd-elasticsearch
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluentd-elasticsearch
image: nginx:1.7.9
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi

滚动更新

问:kind 类型是 Pod 会滚动更新吗?

答:Pod 类型本身不支持滚动更新。滚动更新是一种用于更新 Deployment、StatefulSet 和 DaemonSet 等控制器管理的 Pod 集合的策略。这些控制器通过创建、更新和删除 Pod 来管理应用程序的运行状态。

当您修改 Deployment 中的 Pod 模板时,可以启用滚动更新策略,以确保在进行更新时不会中断应用程序的可用性。滚动更新会逐步替换旧的 Pod 实例,使新的 Pod 实例逐渐接管流量和工作负载,从而实现平滑的应用程序更新。

在滚动更新期间,Kubernetes 控制平面会根据指定的策略控制新旧 Pod 实例之间的切换速度。这些策略可以包括最大不可用 Pod 数量、最大搜索时间等,以确保在更新过程中保持应用程序的稳定性。

请注意,滚动更新仅适用于使用 Deployment、StatefulSet 和 DaemonSet 等控制器进行管理的 Pod,而直接使用 Pod 资源的更改不会触发滚动更新。

References